#pragma once

#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include "Log.hpp"
#include "Socket.hpp"
#include "Epoller.hpp"
#include "Comm.hpp"

// 声明，因为它们之间会有对应类型的变量，不声明将编译不了，即报错
class Connection;
class EventLoop;

// 函数类型
using func_t = std::function<void(std::weak_ptr<Connection>)>; // 需要在前面声明Connection，否则会报错

// 管理链接，包含链接的缓冲区和对应的回调函数
class Connection
{
public:
    Connection(int fd)
        : _socket_fd(fd)
    {
    }

    // 设置三个处理方法的回调函数
    void SetHandler(func_t receive_cb, func_t send_cb, func_t except_cb)
    {
        _receive_cb = receive_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    // 设置与EventLoop之间的关系，因为可能会通过Connection对象调用EventLoop中的函数
    void SetEventLoopWeakPtr(std::weak_ptr<EventLoop> event_loop_ptr)
    {
        _event_loop_ptr = event_loop_ptr;
    }

    void SetIpAndPort(const std::string &ip, uint16_t port)
    {
        _ip = ip;
        _port = port;
    }

    int GetSocketFd()
    {
        return _socket_fd;
    }

    void AppendInbuffer(const std::string &info)
    {
        _in_buffer += info;
    }

    void AppendOutbuffer(const std::string &info)
    {
        _out_buffer += info;
    }

    std::string &GetInbuffer() // 返回类型需要用&，因为会对其进行修改，如删除数据
    {
        return _in_buffer;
    }

    std::string &GetOutbuffer()
    {
        return _out_buffer;
    }

public:
    func_t _receive_cb;                       // 接收
    func_t _send_cb;                          // 发送
    func_t _except_cb;                        // 异常处理
    std::weak_ptr<EventLoop> _event_loop_ptr; // 方便回调
    // 放于公开，方便使用
    std::string _ip;
    uint16_t _port;

private:
    int _socket_fd;          // 链接的套接字对象
    std::string _in_buffer;  // 接收缓冲区
    std::string _out_buffer; // 发送缓冲区
};

// enable_shared_from_this:可以提供返回当前对象的this对应的shared_ptr
// noncopy为了使该类型的对象不能被拷贝，防止因为拷贝或者其他情况而出错
class EventLoop : public std::enable_shared_from_this<EventLoop>, public noncopy
{
public:
    static const uint16_t default_port = 8080;
    static const int max_events = 64;          // 事件最大数
    uint32_t event_in = (EPOLLIN | EPOLLET);   // 关系in操作，ET模式
    uint32_t event_out = (EPOLLOUT | EPOLLET); // 关系out操作，ET模式
    static const int buff_size = 1024;         // 缓冲区大小

    EventLoop(func_t data_processing, std::string ip = "0.0.0.0", uint16_t port = default_port)
        : _data_processing(data_processing),
          _ip(ip),
          _port(port),
          _listensock_ptr(new Socket()),
          _epoller_ptr(new Epoller()),
          _quit(true)
    {
    }

    void Initial()
    {
        _listensock_ptr->CreateSock();
        if (!SetNonBlockOrDie(_listensock_ptr->GetSockFd())) // 设置非阻塞，如果不成功，后续操作也没必要进行，因为会阻塞住
        {
            lg(Error, "Initial: set _listensock_ptr nonblock error");
            exit(-1);
        }
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        lg(Info, "create listen sock success, fd: %d", _listensock_ptr->GetSockFd());
        // 对监听套接字添加对应的处理函数和关系事件等等，否则没有事件等待，程序无法进行
        AddConnection(_listensock_ptr->GetSockFd(), event_in, std::bind(&EventLoop::Accept, this, std::placeholders::_1), nullptr, nullptr, _ip, _port);
    }

    // 添加链接，单独封装，方便处理
    void AddConnection(int socket_fd, uint32_t event, func_t receive_cb, func_t send_cb, func_t except_cb, const std::string &ip, uint16_t port)
    {
        // std::cout<<1<<std::endl;
        std::shared_ptr<Connection> conn(new Connection(socket_fd));
        // std::cout<<2<<std::endl;
        conn->SetEventLoopWeakPtr(shared_from_this()); // 设置回调

        // std::cout<<3<<std::endl;
        conn->SetHandler(receive_cb, send_cb, except_cb);
        // std::cout<<4<<std::endl;
        conn->SetIpAndPort(ip, port);
        // std::cout<<5<<std::endl;
        _connection.insert({socket_fd, conn}); // 方便查找
        // std::cout<<6<<std::endl;
        _epoller_ptr->EpollControl(EPOLL_CTL_ADD, socket_fd, event); // 进行关心
        lg(Debug, "add a new connection[%s:%d], fd: %d", ip.c_str(), port, socket_fd);
    }

    // 链接管理器，监听套接字_listensock_ptr接收其他套接字连接
    void Accept(std::weak_ptr<Connection> conne)
    {
        // std::string clientip;
        // uint16_t clientport;
        // int fd = _listensock_ptr->Accept(&clientip, &clientport);
        // if (fd < 0)
        //     return;
        // if (!SetNonBlockOrDie(fd))
        // {
        //     lg(Error, "Accept: set fd nonblock error");
        //     exit(-1);
        // }
        // AddConnection(fd, event_in, std::bind(&EventLoop::Receive, this, std::placeholders::_1),
        //               std::bind(&EventLoop::Sender, this, std::placeholders::_1),
        //               std::bind(&EventLoop::Exception, this, std::placeholders::_1), clientip, clientport);

        // 监听套接字_listensock_ptr是被设置为非阻塞的，所以，它可以一直监听，知道没有或者出错
        auto connection = conne.lock(); // 获取对应的shared_ptr
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int fd = ::accept(connection->GetSocketFd(), (struct sockaddr *)&peer, &len); // 调用系统的
            if (fd > 0)
            {
                uint16_t peerport = ntohs(peer.sin_port);
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                lg(Debug, "get a new client, get info-> [%s:%d], sockfd : %d", ipbuf, peerport, fd);
                if (!SetNonBlockOrDie(fd))
                {
                    lg(Error, "Accept: set fd nonblock error");
                    exit(-1);
                }
                // 添加链接
                AddConnection(fd, event_in, std::bind(&EventLoop::Receive, this, std::placeholders::_1),
                              std::bind(&EventLoop::Sender, this, std::placeholders::_1),
                              std::bind(&EventLoop::Exception, this, std::placeholders::_1), ipbuf, peerport);
            }
            else
            {
                if (errno == EWOULDBLOCK) // 没有就绪的了，可以退出
                    break;
                else if (errno == EINTR) // 被中断，重新来
                    continue;
                else // 出错，不再继续了
                    break;
            }
        }
    }

    // 事件管理器，负责接收数据，只负责接收，不关心数据的格式
    void Receive(std::weak_ptr<Connection> con)
    {
        if (con.expired())
            return;
        auto conne = con.lock();
        char buffer[buff_size];
        int fd = conne->GetSocketFd();
        // ssize_t sz = read(fd, buffer, sizeof(buffer) - 1);
        // if (sz > 0)
        // {
        //     buffer[sz] = 0;
        //     conne->AppendInbuffer(buffer);
        // }
        // else
        // {
        //     conne->_except_cb(conne);
        // }

        // 非阻塞，可进行多次
        while (true)
        {
            memset(buffer, 0, buff_size);
            ssize_t sz = recv(fd, buffer, sizeof(buffer) - 1, 0);
            if (sz > 0)
            {
                buffer[sz] = 0;
                conne->AppendInbuffer(buffer); // 添加到输入缓冲区，数据增加
            }
            else if (sz == 0) // 退出
            {
                lg(Info, "[%s:%d], socket socket fd: %d, recv quit", conne->_ip, conne->_port, fd);
                conne->_except_cb(conne);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK) // 没有就绪的了，可以退出
                    break;
                else if (errno == EINTR) // 被中断，重新来
                    continue;
                else // 出错，交由异常处理函数处理
                {
                    lg(Warning, "[%s:%d], socket fd: %d, recv error", conne->_ip, conne->_port, fd);
                    conne->_except_cb(conne);
                    return;
                }
            }
        }

        // 接收到数据，调用回调函数进行处理
        _data_processing(conne);
    }

    // 负责发送数据，只负责发送，不关心数据的格式
    void Sender(std::weak_ptr<Connection> con)
    {
        if (con.expired())
            return;
        auto conne = con.lock();
        // int fd = conne->GetSocketFd();
        std::string &out_message = conne->GetOutbuffer();

        // ssize_t sz = write(fd, out_message.c_str(), out_message.size());
        // if (sz > 0)
        // {
        //     out_message.erase(0, sz);
        // }

        // 非阻塞，可进行多次
        while (true)
        {
            // 发送数据，知道数据发送完成
            ssize_t sz = send(conne->GetSocketFd(), out_message.c_str(), out_message.size(), 0);
            if (sz > 0)
            {
                out_message.erase(0, sz);
                if (out_message.empty()) // 没有数据可发送了，可退出
                    return;
            }
            else if (sz == 0) // 退出
            {
                // lg(Info, "[%s:%d], socket fd: %d, send quit", conne->_ip, conne->_port, fd);
                lg(Info, "[%s:%d], socket fd: %d, send quit", conne->_ip, conne->_port, conne->GetSocketFd());
                conne->_except_cb(conne);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK) // 没有就绪的了，可以退出
                    break;
                else if (errno == EINTR) // 被中断，重新来
                    continue;
                else // 出错，交由异常处理函数处理
                {
                    // lg(Warning, "[%s:%d], socket fd: %d, send error", conne->_ip, conne->_port, fd);
                    lg(Warning, "[%s:%d], socket fd: %d, send error", conne->_ip, conne->_port, conne->GetSocketFd());
                    conne->_except_cb(conne);
                    return;
                }
            }
        }

        // 如果还有数据没发送完，开启写事件关心，否则将写事件关心关掉
        if (!out_message.empty())
            SetReadWrite(conne->GetSocketFd(), true, true);
        else
            SetReadWrite(conne->GetSocketFd(), true, false);
    }

    // 异常处理
    void Exception(std::weak_ptr<Connection> con)
    {
        if (con.expired())
            return;
        auto conne = con.lock();
        int fd = conne->GetSocketFd();
        lg(Error, "Exception handler, [%s:%d], fd: %d, errno: %d, error string: %s", conne->_ip, conne->_port, fd, errno, strerror(errno)); // 链接的ip和端口号也带上
        _epoller_ptr->EpollControl(EPOLL_CTL_DEL, fd, 0);                                                                                   // epool关闭
        close(fd);                                                                                                                          // 套接字关闭
        _connection.erase(fd);                                                                                                              // 映射关心去除
    }

    // 设置读写关心
    void SetReadWrite(int fd, bool readable, bool writeable)
    {
        uint32_t events = ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->EpollControl(EPOLL_CTL_MOD, fd, events); // 修改epoll
    }

    // 检测是否存在连接
    bool IsFdConnectionSafe(int fd)
    {
        auto iter = _connection.find(fd);
        if (iter == _connection.end())
            return false;
        return true;
    }

    // 派发任务
    void Dispatch()
    {
        int num = _epoller_ptr->EpollWait(_events, max_events); // 等待事件
        for (int i = 0; i < num; ++i)
        {
            int fd = _events[i].data.fd;
            if ((_events[i].events & EPOLLIN) && IsFdConnectionSafe(fd))
            {
                if (_connection[fd]->_receive_cb) // 需要使用shared_ptr，否则_receive_cb无法使用
                    _connection[fd]->_receive_cb(_connection[fd]);
                else
                    _connection[fd]->_except_cb(_connection[fd]);
            }
            else if ((_events[i].events & EPOLLOUT) && IsFdConnectionSafe(fd))
            {
                if (_connection[fd]->_send_cb)
                    _connection[fd]->_send_cb(_connection[fd]);
                else
                    _connection[fd]->_except_cb(_connection[fd]);
            }
        }
    }

    void Loop()
    {
        _quit = false; // 如想要在程序运行期间停止，可增加一个修改_quit的函数对其修改
        while (!_quit)
        {
            Dispatch();
            PrintConnectionInbuffer();
        }
        _quit = true;
    }

    void PrintConnectionInbuffer()
    {
        std::cout << "all connection socket fd and inbuff:" << std::endl;
        for (auto &iter : _connection)
        {
            std::cout << iter.first << ": ";
            std::cout << iter.second->GetInbuffer() << std::endl;
        }
    }

    ~EventLoop()
    {
        _listensock_ptr->Close();
    }

private:
    std::shared_ptr<Socket> _listensock_ptr; // 监听套接字
    std::shared_ptr<Epoller> _epoller_ptr;   // 内核，epoll封装
    uint16_t _port;
    std::string _ip;
    bool _quit;                                                       // 是否停止运行
    std::unordered_map<int, std::shared_ptr<Connection>> _connection; // 维持套接字和链接之间的映射，方便查找
    struct epoll_event _events[max_events];                           // 事件
    func_t _data_processing;                                          // 上层处理数据，回调
};